package com.pan.download5.config;

import com.pan.download5.config.entity.LineInfo;
import com.pan.download5.config.entity.LineType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public enum LineConfig {

    INSTANCE;

    private final String lineExpression = "/panqian/lines/line";
    private final String configExpression = "/panqian/configs/config";

    private List<LineInfo> lineInfoList;
    private Map<String, LineType> configTypeMap;

    private LineConfig() {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = null;
        try {
            documentBuilder = documentBuilderFactory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        }
        //调用配置类
        String lineConfigPath = ApplicationConfig.INSTANCE.getLineConfigPath();
        URL fileUrl = LineConfig.class.getClassLoader().getResource(lineConfigPath);
        String filePath = fileUrl.getFile();
        try {
            filePath = URLDecoder.decode(filePath, Charset.defaultCharset().toString());
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        Document document;
        try {
            document = documentBuilder.parse(filePath);
            document.normalizeDocument();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        this.lineInfoList = new ArrayList<>();
        this.configTypeMap = new HashMap<>();
        NodeList lineNodeList = null;
        NodeList configTypeNodeList = null;
        try {
            lineNodeList = (NodeList) XPathFactory.newInstance().newXPath().evaluate(lineExpression, document, XPathConstants.NODESET);
            configTypeNodeList = (NodeList) XPathFactory.newInstance().newXPath().evaluate(configExpression, document, XPathConstants.NODESET);
        } catch (XPathExpressionException e) {
            throw new RuntimeException(e);
        }
        //初始化configTypeMap
        for (int i = 0; i < configTypeNodeList.getLength(); i++) {
            Element configTypeElement = (Element) configTypeNodeList.item(i);
            LineType lineType = elementMapping(LineType.class, configTypeElement);
            configTypeMap.put(lineType.getType(), lineType);
        }
        //初始化lineInfoList
        for (int i = 0; i < lineNodeList.getLength(); i++) {
            Element lineElement = (Element) lineNodeList.item(i);
            LineInfo lineInfo = parseLine(lineElement, this.configTypeMap);
            lineInfoList.add(lineInfo);
        }

    }


    private LineInfo parseLine(Element lineElement, Map<String, LineType> typeMap) {

        Function<String, Element> getElementByName = tagName -> {
            NodeList nl = lineElement.getElementsByTagName(tagName);
            for (int i = 0; i < nl.getLength(); i++) {
                if (nl.item(i).getNodeType() == Node.ELEMENT_NODE)
                    return (Element) nl.item(i);
            }
            throw new RuntimeException("LineElement没找到" + tagName + "节点，在"+lineElement.getTextContent());
        };
        Function<String, String> getTextContentByName = tageName -> getElementByName.apply(tageName).getTextContent().trim();
        String urlTagName = "url";
        String typeTagName = "type";
        String infoTagName = "info";

        String url = getTextContentByName.apply(urlTagName);
        String type = getTextContentByName.apply(typeTagName);
        Element infoElement = getElementByName.apply(infoTagName);
        Map<String, String> map = childElementToMap(infoElement);
        if (!typeMap.containsKey(type)) {
            throw new RuntimeException("The LineConfigType not found: " + type);
        }
        LineType configType = typeMap.get(type);
        LineInfo result = new LineInfo(url, map, configType);
        return result;
    }

//    public boolean hasConfigType(String type) {
//        return this.configTypeMap.containsKey(type);
//    }
//
//    public LineConfigType getConfigType(String type) {
//        return this.configTypeMap.get(type);
//    }

    private <T> T copy(T obj){
        try {
            ByteArrayOutputStream byteArrayOutputStream= new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(obj);
            byte[] byteArray = byteArrayOutputStream.toByteArray();
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return (T) objectInputStream.readObject();
        }catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

    }
//    public Map<String, LineType> getTypeMap(){
//        return copy(this.configTypeMap);
//    }
//
//    public List<LineInfo> getLineList(){
//        return copy(this.lineInfoList);
//    }
    public LineInfo findLine(String url) {
        for (LineInfo li : lineInfoList) {
            if (url.indexOf(li.getUrl()) != -1) {
                return li;
            }
        }
        return null;
    }


    //将子节点转换为key为tagName，value为textContent的Map
    private Map<String, String> childElementToMap(Element element) {
        Map<String, String> resultMap = new HashMap<>();
        element.normalize();
        NodeList childNodes = element.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element e = (Element) node;
                String tagName = e.getTagName();
                String textContent = e.getTextContent();
                resultMap.put(tagName, textContent);
            }
        }
        return resultMap;
    }

    //根据Class将xml的element映射为java类
    private <T> T elementMapping(String className, Element element) {
        try {
            Class<T> aClass = (Class<T>) Class.forName(className);
            return elementMapping(aClass, element);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    private <T> T elementMapping(Class<T> clazz, Element element) {
        Field[] fields = clazz.getDeclaredFields();
        Map<String, String> infoMap = childElementToMap(element);
        T result;
        try {
            Constructor<T> constructor = clazz.getConstructor();
            result = constructor.newInstance();
            for (Field field : fields) {
                field.setAccessible(true);
                String name = field.getName();
                if (infoMap.containsKey(name)) {
                    String value = infoMap.get(name);
                    value = value.trim();
                    field.set(result, value);
                }
            }
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    public static void main(String[] args) {
        List<LineInfo> lineList = INSTANCE.lineInfoList;
        for (LineInfo li: lineList){
            System.out.println(li.toString());
        }
    }
//
//    public Line findByUrl(String storyDirectoryUrl){
//        if (lineList.size()==0) return null;
//        return lineList.stream()
//                .filter(line -> storyDirectoryUrl.indexOf(line.getUrl())!= -1)
//                .findFirst().orElse(null);
//    }
//

//    public List<Class<? extends Search>> getSearchClassList(){
//        List<Class<? extends Search>> searchClassList = lineList.stream().map(Line::getSearchClass).collect(Collectors.toList());
//        return searchClassList;
//    }
//
//    private Line toLine(Element lineElement){
//        String nodeName = lineElement.getTagName();
//        if (!nodeName.equals("line")){
//            throw new RuntimeException("This isn't LineNode. 这不是LineNode。");
//        }
//        NodeList childNodes = lineElement.getChildNodes();
//        String url = null;
//        String directoryClassName = null;
//        String pageClassName = null;
//        String searchClassName = null;
//        for (int i = 0; i < childNodes.getLength(); i++) {
//            Node item = childNodes.item(i);
//            if (item.getNodeType() != 1) continue;
//            Element element = (Element) item;
//            String tagName = element.getTagName();
//            if (tagName.equals("url")){
//                url = element.getTextContent().trim();
//            }else if (tagName.equals("directory")){
//                directoryClassName = element.getTextContent().trim();
//            }else if (tagName.equals("page")){
//                pageClassName = element.getTextContent().trim();
//            }else if (tagName.equals("search")){
//                searchClassName = element.getTextContent().trim();
//            }else {
//                continue;
//            }
//        }
//        Line line = new Line(url,directoryClassName,pageClassName,searchClassName);
//        return line;
//    }

//    public static void main(String[] args) throws XPathExpressionException {
//        //测试 findUrl 成功
//        Line line = INSTANCE.findByUrl("http://www.xheiyan1145.info/tangrendecanzhuo/");
//        System.out.println(line==null?null:line.toString());
//
//        测试 xml -> Line 成功
//        Document doc = INSTANCE.document;
//        XPathFactory xPathFactory = XPathFactory.newInstance();
//        XPath xPath = xPathFactory.newXPath();
//        NodeList nodeList = (NodeList) xPath.evaluate("/panqian/lines/line",doc, XPathConstants.NODESET);
//        Line[] lines = new Line[nodeList.getLength()];
//        for (int i = 0; i < nodeList.getLength(); i++) {
//            lines[i] = INSTANCE.toLine((Element) nodeList.item(i));
//            System.out.println(lines[i].toString());
//        }
//
//    }

}
